package cloud.shyidong.tenant.service.impl;

import cloud.shyidong.core.Result;
import cloud.shyidong.core.constant.OrderConstant;
import cloud.shyidong.core.context.TenantContext;
import cloud.shyidong.core.context.TenantContextHolder;
import cloud.shyidong.core.exception.BusinessException;
import cloud.shyidong.core.request.LongIdRequest;
import cloud.shyidong.core.service.AbstractTenantService;
import cloud.shyidong.core.util.DateUtils;
import cloud.shyidong.core.util.ObjectUtils;
import cloud.shyidong.core.util.StringUtils;
import cloud.shyidong.core.wxpay.WxpayConfig;
import cloud.shyidong.tenant.constant.OrderStatusConstant;
import cloud.shyidong.tenant.constant.TenantStatusConstant;
import cloud.shyidong.tenant.dao.TenantOrderDao;
import cloud.shyidong.tenant.dao.TenantOrderNoDao;
import cloud.shyidong.tenant.dao.TenantTenantDao;
import cloud.shyidong.tenant.entity.TenantOrderEntity;
import cloud.shyidong.tenant.entity.TenantOrderNoEntity;
import cloud.shyidong.tenant.entity.TenantTenantEntity;
import cloud.shyidong.tenant.request.TenantOrderInsertRequest;
import cloud.shyidong.tenant.request.TenantOrderPageRequest;
import cloud.shyidong.tenant.request.TenantOrderUpdateRequest;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.util.PemUtil;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import com.wechat.pay.java.service.payments.nativepay.model.Amount;
import com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest;
import com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayOutputStream;
import java.security.PrivateKey;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 租户订单业务处理
 * </p>
 *
 * @author syd
 * @since 2023-09-19 11:53:35
 */
@Service
@Slf4j
public class OrderService extends AbstractTenantService<TenantOrderDao, TenantOrderEntity, LongIdRequest, TenantOrderInsertRequest, TenantOrderUpdateRequest, TenantOrderPageRequest> {
    
    private Integer orderTimeout;
    
    private WxpayConfig wxpayConfig;
    
    private RedisTemplate<String, String> redisTemplate;
    
    private TenantOrderNoDao tenantOrderNoDao;
    
    private TenantTenantDao tenantTenantDao;
    
    private Config config;
    
    @Value("${order.timeout}")
    public void setOrderTimeout(Integer orderTimeout) {
        this.orderTimeout = orderTimeout;
    }
    
    @Autowired
    public void setWxpayConfig(WxpayConfig wxpayConfig) {
        this.wxpayConfig = wxpayConfig;
    }
    
    @Autowired
    public void setRedisTemplate(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    
    @Autowired
    public void setTenantOrderNoDao(TenantOrderNoDao tenantOrderNoDao) {
        this.tenantOrderNoDao = tenantOrderNoDao;
    }
    
    @Autowired
    public void setTenantTenantDao(TenantTenantDao tenantTenantDao) {
        this.tenantTenantDao = tenantTenantDao;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public TenantOrderEntity insert(TenantOrderInsertRequest insertRequest) {
        
        TenantContext tenantContext = TenantContextHolder.getTenantContext();
        if (ObjectUtils.isNull(tenantContext)) {
            throw BusinessException.from("此地址需要认证后才能访问");
        }
        if (ObjectUtils.isNull(tenantContext.getTenantId())) {
            throw BusinessException.from("非法操作");
        }
        if (ObjectUtils.isNull(tenantContext.getUserId())) {
            throw BusinessException.from("非法操作");
        }
        
        QueryWrapper<TenantOrderEntity> tenantOrderEntityQueryWrapper = new QueryWrapper<>();
        tenantOrderEntityQueryWrapper.eq("tenant_id", tenantContext.getTenantId());
        tenantOrderEntityQueryWrapper.eq("status", OrderStatusConstant.UNPAID);
        if (getDao().selectCount(tenantOrderEntityQueryWrapper) > 0) {
            throw BusinessException.from("你还有未支付订单处理");
        }
        
        Date now = DateUtils.getNow();
        Long userId = tenantContext.getUserId();
        
        //生成订单编号
        TenantOrderNoEntity tenantOrderNoEntity = new TenantOrderNoEntity();
        tenantOrderNoEntity.setCurrenttime(System.currentTimeMillis());
        tenantOrderNoDao.insert(tenantOrderNoEntity);
        StringBuilder orderNo = new StringBuilder();
        orderNo.append(tenantOrderNoEntity.getCurrenttime());
        orderNo.append(tenantOrderNoEntity.getId());
        
        //查询租户信息
        TenantTenantEntity tenantTenantEntity = tenantTenantDao.selectById(tenantContext.getTenantId());
        
        //订单信息
        TenantOrderEntity tenantOrderEntity = new TenantOrderEntity();
        tenantOrderEntity.setTenantId(tenantContext.getTenantId());
        tenantOrderEntity.setOrderNo(orderNo.toString());
        tenantOrderEntity.setMoney(tenantTenantEntity.getPrice());
        tenantOrderEntity.setPayMoney(tenantOrderEntity.getMoney());
        tenantOrderEntity.setCreateTime(now);
        tenantOrderEntity.setCreateUser(userId);
        tenantOrderEntity.setUpdateTime(now);
        tenantOrderEntity.setUpdateUser(userId);
        getDao().insert(tenantOrderEntity);
        
        return tenantOrderEntity;
        
    }
    
    /**
     * 验证预支付
     *
     * @param longIdRequest
     * @return
     */
    public Result beforePrepay(LongIdRequest longIdRequest) {
        
        if (ObjectUtils.isNull(longIdRequest)) {
            throw BusinessException.from("未查找到订单");
        }
        
        TenantOrderEntity tenantOrderEntity = getDao().selectById(longIdRequest.getId());
        
        if (ObjectUtils.isNull(tenantOrderEntity)) {
            throw BusinessException.from("未查找到订单");
        }
        
        //验证会员订单
        Long tenantId = TenantContextHolder.getTenantContext().getTenantId();
        if (tenantId.longValue() != tenantOrderEntity.getTenantId()) {
            throw BusinessException.from("非法操作");
        }
        
        //验证状态
        if (tenantOrderEntity.getStatus().intValue() == OrderStatusConstant.PAID) {
            throw BusinessException.from("订单已支付");
        } else if (tenantOrderEntity.getStatus().intValue() == OrderStatusConstant.TIMEOUT) {
            throw BusinessException.from("订单已超时");
        } else if (tenantOrderEntity.getStatus().intValue() == OrderStatusConstant.CANCEL) {
            throw BusinessException.from("订单已取消");
        }
        
        //验证订单是否超时
        Date timeOut = DateUtils.getAfterDate(tenantOrderEntity.getCreateTime(), null, null, orderTimeout);
        if (timeOut.getTime() < DateUtils.getNow().getTime()) {
            tenantOrderEntity.setStatus(OrderStatusConstant.TIMEOUT);
            tenantOrderEntity.setUpdateTime(DateUtils.getNow());
            tenantOrderEntity.setUpdateUser(TenantContextHolder.getTenantContext().getUserId());
            int result = getDao().updateById(tenantOrderEntity);
            if (result == 0) {
                throw BusinessException.from("支付失败");
            }
            
            wxCancel(tenantOrderEntity.getOrderNo());
            
            throw BusinessException.from("订单已超时");
            
        }
        
        //查询预支付codeUrl
        if (StringUtils.isEmpty(tenantOrderEntity.getCodeUrl())) {
            if (ObjectUtils.isNull(config)) {
                log.info("wxpayConfig >>> {}", JSONObject.toJSONString(wxpayConfig));
                config = new RSAAutoCertificateConfig.Builder()
                        .merchantId(wxpayConfig.getMchId())
                        .privateKeyFromPath(wxpayConfig.getPrivateKeyPath())
                        .merchantSerialNumber(wxpayConfig.getMerchantSerialNumber())
                        .apiV3Key(wxpayConfig.getApiV3Key())
                        .build();
            }
            NativePayService nativePayService = new NativePayService.Builder().config(config).build();
            PrepayRequest prepayRequest = new PrepayRequest();
            Amount amount = new Amount();
            amount.setTotal(tenantOrderEntity.getPayMoney().intValue());
            prepayRequest.setAmount(amount);
            prepayRequest.setAppid(wxpayConfig.getAppId());
            prepayRequest.setMchid(wxpayConfig.getMchId());
            prepayRequest.setDescription(tenantOrderEntity.getOrderNo());
            prepayRequest.setNotifyUrl(wxpayConfig.getNotifyUrl() + "/tenant/tenant-order/notify");
            prepayRequest.setOutTradeNo(tenantOrderEntity.getOrderNo());
            
            //调用微信预支付接口产生二维码
            PrepayResponse prepayResponse = nativePayService.prepay(prepayRequest);
            tenantOrderEntity.setCodeUrl(prepayResponse.getCodeUrl());
            tenantOrderEntity.setUpdateTime(DateUtils.getNow());
            tenantOrderEntity.setUpdateUser(TenantContextHolder.getTenantContext().getUserId());
            int result = getDao().updateById(tenantOrderEntity);
            if (result == 0) {
                throw BusinessException.from("支付失败");
            }
        }
        log.info("prepay url >>> {}", tenantOrderEntity.getCodeUrl());
        
        return Result.ofData(tenantOrderEntity);
    }
    
    /**
     * 微信关单
     *
     * @param orderNo
     */
    private void wxCancel(String orderNo) {
        
        log.info("微信开始取消订单 >>> {}", orderNo);
        try {
            PrivateKey privateKey = PemUtil.loadPrivateKeyFromPath(wxpayConfig.getPrivateKeyPath());
            PrivateKeySigner privateKeySigner = new PrivateKeySigner(wxpayConfig.getMerchantSerialNumber(), privateKey);
            WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(wxpayConfig.getMchId(), privateKeySigner);
            CertificatesManager certificatesManager = CertificatesManager.getInstance();
            certificatesManager.putMerchant(wxpayConfig.getMchId(), wechatPay2Credentials, wxpayConfig.getApiV3Key().trim().getBytes());
            Verifier verifier = certificatesManager.getVerifier(wxpayConfig.getMchId());
            WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create().withMerchant(wxpayConfig.getMchId(), wxpayConfig.getMerchantSerialNumber(), privateKey).withValidator(new WechatPay2Validator(verifier));
            CloseableHttpClient httpClient = builder.build();
            
            HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/" + orderNo + "/close");
            httpPost.addHeader("Accept", "application/json");
            httpPost.addHeader("Content-type", "application/json; charset=utf-8");
            
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectMapper objectMapper = new ObjectMapper();
            
            ObjectNode rootNode = objectMapper.createObjectNode();
            rootNode.put("mchid", wxpayConfig.getMchId());
            
            objectMapper.writeValue(bos, rootNode);
            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
            CloseableHttpResponse closeableHttpResponse = httpClient.execute(httpPost);
            int code = closeableHttpResponse.getStatusLine().getStatusCode();
            if (code == HttpStatus.SC_OK) {
                log.info("微信取消订单成功 >>> {}", orderNo);
            } else if (code == HttpStatus.SC_NO_CONTENT) {
                log.info("微信取消订单成功 >>> {}", orderNo);
            } else {
                log.info("微信取消订单失败 >>> {}，响应码 >>> {}", orderNo, code);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        
    }
    
    /**
     * 更新订单状态为已支付
     *
     * @param plainText
     */
    @Transactional(rollbackFor = Exception.class)
    public void updatePaymentByOrderNo(String plainText) {
        
        Map<String, Object> plainTextMap = JSONObject.parseObject(plainText, Map.class);
        //订单号
        String orderNo = (String) plainTextMap.get("out_trade_no");
        
        String value = redisTemplate.opsForValue().get(String.format(OrderConstant.ORDER_NOTIFY_KEY, orderNo));
        while (StringUtils.isNotEmpty(value)) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                log.error(e.getMessage(), e);
            }
        }
        
        try {
            
            redisTemplate.opsForValue().set(String.format(OrderConstant.ORDER_NOTIFY_KEY, orderNo), orderNo, 1, TimeUnit.MINUTES);
            
            QueryWrapper<TenantOrderEntity> orderOrderEntityQueryWrapper = new QueryWrapper<>();
            orderOrderEntityQueryWrapper.eq("order_no", orderNo);
            orderOrderEntityQueryWrapper.eq("status", OrderStatusConstant.PAID);
            if (getDao().selectCount(orderOrderEntityQueryWrapper) > 0) {
                return;
            }
            
            Date now = DateUtils.getNow();
            
            //查询订单信息
            TenantOrderEntity tenantOrderEntity = getDao().selectOne(new QueryWrapper<TenantOrderEntity>().eq("order_no", orderNo));
            
            //修改订单信息
            tenantOrderEntity.setPayTime(now);
            tenantOrderEntity.setStatus(OrderStatusConstant.PAID);
            getDao().updateById(tenantOrderEntity);
            
            //修改租户的状态为已认证
            TenantTenantEntity tenantTenantEntity = tenantTenantDao.selectById(tenantOrderEntity.getTenantId());
            
            //已认证续约
            if (tenantTenantEntity.getStatus().intValue() == TenantStatusConstant.AUTH) {
                tenantTenantEntity.setEndTime(DateUtils.getAfterYear(tenantTenantEntity.getEndTime()));
            }
            //未认证进行认证或已过期续约
            else {
                tenantTenantEntity.setStatus(TenantStatusConstant.AUTH);
                tenantTenantEntity.setEndTime(DateUtils.getAfterYear());
            }
            
            tenantTenantDao.updateById(tenantTenantEntity);
            
            //业务编号
            String transactionId = (String) plainTextMap.get("transaction_id");
            //支付类型
            String tradeType = (String) plainTextMap.get("trade_type");
            //支付状态
            String tradeState = (String) plainTextMap.get("trade_state");
            //订单金额
            Map<String, Object> amount = (Map<String, Object>) plainTextMap.get("amount");
            //总金额
            Object totalObj = amount.get("total");
            Integer total = null;
            if (totalObj instanceof Double) {
                total = ((Double) totalObj).intValue();
            } else {
                total = (Integer) totalObj;
            }
            //用户支付金额
            Object payerTotalObj = amount.get("payer_total");
            Integer payerTotal = null;
            if (totalObj instanceof Double) {
                payerTotal = ((Double) payerTotalObj).intValue();
            } else {
                payerTotal = (Integer) payerTotalObj;
            }
            
            //TODO 支付日志
//            SystemPaymentLogEntity systemPaymentLogEntity = new SystemPaymentLogEntity();
//            systemPaymentLogEntity.setOrderNo(orderNo);
//            systemPaymentLogEntity.setTransactionId(transactionId);
//            systemPaymentLogEntity.setTradeType(tradeType);
//            systemPaymentLogEntity.setTradeState(tradeState);
//            systemPaymentLogEntity.setTotal(total);
//            systemPaymentLogEntity.setPayerTotal(payerTotal);
//            systemPaymentLogEntity.setContent(plainText);
//            systemPaymentLogEntity.setCreateTime(DateUtils.getNow());
//            systemPaymentLogDao.insert(systemPaymentLogEntity);
//
        } finally {
            redisTemplate.delete(String.format(OrderConstant.ORDER_NOTIFY_KEY, orderNo));
        }
        
    }
    
}
